/** \file
* POSIX-specific implementation of the Mutex class.
* This file is part of Chorus, available at
* http://code.google.com/p/chorus-twitter/ . This file and all associated files
* and derivative works are bound by the terms of the LGPL 3.0 license,
* available at http://www.gnu.org/licenses/lgpl-3.0-standalone.html .
* \par Subversion URL:
* \$URL: http://chorus-twitter.googlecode.com/svn/branches/cpp/src/system/posix/Mutex.cpp $
* \version \$Rev: 153 $
* \date    \$Date: 2010-04-06 07:43:42 +0000 (Tue, 06 Apr 2010) $
*/

#define NEEDS_POSIX
#include "config.h"

#include <errno.h>

#include "system/DebugConsole.hpp"
#include "system/Timer.hpp"
#include "system/Mutex.hpp"

using namespace std;

Mutex::Mutex()
{
	// Create the pthread mutex and its attributes. Mutex code has to use
	// unguarded debug console calls so that they in turn don't call mutex (in
	// case mutexes are screwed up).

	pthread_mutexattr_t attrs;
	int result;
	if (result = pthread_mutexattr_init(&attrs))
	{
		UNGUARDED_PDEBUG_C_CODE("pthread_mutexattr_init failed", result);
		return;
	}

	// Make sure that the mutex is set to check for error conditions.
	if (result = pthread_mutexattr_settype(&attrs, PTHREAD_MUTEX_ERRORCHECK))
	{
		UNGUARDED_PDEBUG_C_CODE("pthread_mutexattr_settype failed", result);
		goto fail;
	}

	if (result = pthread_mutex_init(&mutex, &attrs))
	{
		UNGUARDED_PDEBUG_C_CODE("pthread_mutex_init failed", result);
		goto fail;
	}

fail:
	if (result = pthread_mutexattr_destroy(&attrs))
		UNGUARDED_PDEBUG_C_CODE("pthread_mutexattr_destroy failed", result);
}

Mutex::~Mutex()
{
	// Destroy the mutex.
	int result;
	if (result = pthread_mutex_destroy(&mutex))
		UNGUARDED_PDEBUG_C_CODE("pthread_mutex_destroy failed", result);
}

bool Mutex::lock()
{
	// Do a simple mutex lock.
	int result;
	if (result = pthread_mutex_lock(&mutex))
	{
		UNGUARDED_PDEBUG_C_CODE("pthread_mutex_lock failed", result);
		return false;
	}
	return true;
}

#ifdef __unix__
// The following code only really works in linux. In mac, the timed lock
// function doesn't exist. Total garbage.

bool Mutex::lock(unsigned timeoutms)
{
	return lock(Timer::msToTs(timeoutms));
}

bool Mutex::lock(timespec timeoutts)
{
	// Do a timed lock.
	int result = pthread_mutex_timedlock(&mutex, &timeoutts);
	switch (result)
	{
	case 0:
		return true;

	case ETIMEDOUT:
		UNGUARDED_PDEBUG("Warning - mutex lock timeout");
		return false;

	default:
		UNGUARDED_PDEBUG_C_CODE("pthread_mutex_timedlock failed", result);
		return false;
	}
}
#endif // #ifdef __unix__

bool Mutex::unlock()
{
	// Do a simple unlock.
	int result;
	if (result = pthread_mutex_unlock(&mutex))
	{
		UNGUARDED_PDEBUG_C_CODE("pthread_mutex_unlock failed", result);
		return false;
	}
	return true;
}
